<script setup>
import * as THREE from 'three'

import Stats from 'three/addons/libs/stats.module.js'

import { TrackballControls } from 'three/addons/controls/TrackballControls.js'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'
import { onMounted } from 'vue'
import { ref } from 'vue'
import axios from 'axios'

let container, stats
let camera, controls, scene, renderer
let pickingTexture, pickingScene
let highlightBox

const pickingData = []

const pointer = new THREE.Vector2()
const offset = new THREE.Vector3(10, 10, 10)
const clearColor = new THREE.Color()

const dataview = ref(null)

async function init() {
  // container = document.getElementById('container')

  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000)
  camera.position.y = -1200
  camera.position.z = 800

  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xffffff)

  scene.add(new THREE.AmbientLight(0xcccccc))

  const light = new THREE.DirectionalLight( 0xffffff, 3 );
  light.position.set( 0, 500, 2000 );
  scene.add( light );

  const defaultMaterial = new THREE.MeshPhongMaterial({
    color: 0xffffff,
    flatShading: true,
    vertexColors: true,
    shininess: 0,
  })

  // set up the picking texture to use a 32 bit integer so we can write and read integer ids from it
  pickingScene = new THREE.Scene()
  pickingTexture = new THREE.WebGLRenderTarget(1, 1, {
    type: THREE.IntType,
    format: THREE.RGBAIntegerFormat,
    internalFormat: 'RGBA32I',
  })
  const pickingMaterial = new THREE.ShaderMaterial({
    glslVersion: THREE.GLSL3,

    vertexShader: /* glsl */ `
      attribute int id;
      flat varying int vid;
      void main() {
        vid = id;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
    `,

    fragmentShader: /* glsl */ `
      layout(location = 0) out int out_id;
      flat varying int vid;
      void main() {
        out_id = vid;
      }
    `,
  })

  function applyId(geometry, id) {
    const position = geometry.attributes.position
    const array = new Int16Array(position.count)
    array.fill(id)

    const bufferAttribute = new THREE.Int16BufferAttribute(array, 1, false)
    bufferAttribute.gpuType = THREE.IntType
    geometry.setAttribute('id', bufferAttribute)
  }

  function applyVertexColors(geometry, color) {
    const position = geometry.attributes.position
    const colors = []

    for (let i = 0; i < position.count; i++) {
      colors.push(color.r, color.g, color.b)
    }

    geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
  }

  const geometries = []
  const matrix = new THREE.Matrix4()
  const quaternion = new THREE.Quaternion()
  const color = new THREE.Color()

  const res = await axios.get('/0_nodes_SPE10.txt')
  const data = res.data.split('\n').map((line) => line.split(','))
  console.log(data.length)

  // compute node
  let min_x = 10e10
  let max_x = -10e10
  let min_y = 10e10
  let max_y = -10e10
  let min_z = 10e10
  let max_z = -10e10

  for (let i = 0; i < data.length; i++) {
    const x = parseFloat(data[i][0])
    const y = parseFloat(data[i][1])
    const z = parseFloat(data[i][2])

    min_x = min_x > x ? x : min_x
    min_y = min_y > y ? y : min_y
    min_z = min_z > z ? z : min_z
    max_x = max_x < x ? x : max_x
    max_y = max_y < y ? y : max_y
    max_z = max_z < z ? z : max_z
  }
  console.log(min_x, min_y, min_z, max_x, max_y, max_z)

  for (let i = 0; i < 1000; i++) {
    const geometry = new THREE.BoxGeometry()

    // 只保留被5整除
    // if (i % 5 !== 0) {
    //   continue
    // }

    const position = new THREE.Vector3()
    position.x = data[i][0] - min_x - (1 / 2) * (max_x - min_x)
    position.y = data[i][1] - min_y -100
    position.z = data[i][2] - min_z

    const rotation = new THREE.Euler()
    rotation.x = 1 * 2 * Math.PI
    rotation.y = 0 * 2 * Math.PI
    rotation.z = 0 * 2 * Math.PI

    const scale = new THREE.Vector3()
    scale.x = 1
    scale.y = 1
    scale.z = 1

    if (
      true ||
      parseFloat(data[i][2]) === min_z ||
      parseFloat(data[i][2]) === max_z ||
      parseFloat(data[i][1]) === min_y ||
      parseFloat(data[i][1]) === max_y ||
      parseFloat(data[i][0]) === min_x ||
      parseFloat(data[i][0]) === max_x
    ) {
      quaternion.setFromEuler(rotation)
      matrix.compose(position, quaternion, scale)
      geometry.applyMatrix4(matrix)
      applyVertexColors(geometry, color.setHex(Math.random() * 0xffffff))
      applyId(geometry, i)

      geometries.push(geometry)
    }

    pickingData[i] = {
      position: position,
      rotation: rotation,
      scale: scale,
    }
  }

  console.log('finfished')

  const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries)
  scene.add(new THREE.Mesh(mergedGeometry, defaultMaterial))
  pickingScene.add(new THREE.Mesh(mergedGeometry, pickingMaterial))

  highlightBox = new THREE.Mesh(
    new THREE.BoxGeometry(),
    new THREE.MeshLambertMaterial({ color: 0xffff00 }),
  )

  // axes helpers
  scene.add(new THREE.AxesHelper(5))

  scene.add(highlightBox)

  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setAnimationLoop(animate)
  // container.appendChild(renderer.domElement)
  dataview.value.appendChild(renderer.domElement)

  controls = new TrackballControls(camera, renderer.domElement)
  controls.rotateSpeed = 1.0
  controls.zoomSpeed = 1.2
  controls.panSpeed = 0.8
  controls.noZoom = false
  controls.noPan = false
  controls.staticMoving = true
  controls.dynamicDampingFactor = 0.3

  stats = new Stats()
  dataview.value.appendChild(stats.dom)
  // container.appendChild(stats.dom)

  renderer.domElement.addEventListener('pointermove', onPointerMove)
}

function onPointerMove(e) {
  pointer.x = e.clientX
  pointer.y = e.clientY
}

function animate() {
  render()
  stats.update()
}

function pick() {
  // render the picking scene off-screen
  // set the view offset to represent just a single pixel under the mouse
  const dpr = window.devicePixelRatio
  camera.setViewOffset(
    renderer.domElement.width,
    renderer.domElement.height,
    Math.floor(pointer.x * dpr),
    Math.floor(pointer.y * dpr),
    1,
    1,
  )

  // render the scene
  renderer.setRenderTarget(pickingTexture)

  // clear the background to - 1 meaning no item was hit
  clearColor.setRGB(-1, -1, -1)
  renderer.setClearColor(clearColor)
  renderer.render(pickingScene, camera)

  // clear the view offset so rendering returns to normal
  camera.clearViewOffset()

  // create buffer for reading single pixel
  const pixelBuffer = new Int32Array(4)

  // read the pixel
  renderer.readRenderTargetPixelsAsync(pickingTexture, 0, 0, 1, 1, pixelBuffer).then(() => {
    const id = pixelBuffer[0]
    if (id !== -1) {
      // move our highlightBox so that it surrounds the picked object
      const data = pickingData[id]
      highlightBox.position.copy(data.position)
      highlightBox.rotation.copy(data.rotation)
      highlightBox.scale.copy(data.scale).add(offset)
      highlightBox.visible = true
    } else {
      highlightBox.visible = false
    }
  })
}

function render() {
  controls.update()
  pick()
  renderer.setRenderTarget(null)
  renderer.render(scene, camera)
}

onMounted(() => {
  init()
})
</script>

<template>
  <div ref="dataview"></div>
</template>
